package com.chuangfeng.framework.core.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import com.chuangfeng.framework.core.common.config.SystemContext;
import com.chuangfeng.framework.core.dao.BaseDao;
import com.chuangfeng.framework.core.exception.PersistenceException;

/**
 * <BIG>类名：数据访问对象通用基类</BIG>
 * <p>  描述：该类主要继承了DAO通用接口BaseDao,实现了数据实体对象的CURD操作</p>
 * <p>  功能：提供hql分页查询，标准SQL查询，分页查询，条件查询，example分页查询，拷贝更新等功能。</p>
 * 
 * @author xiedy
 * @datetime 2010-11-22
 *
 * @param <T> 实体对象泛型
 */


public abstract class BaseDaoImpl<T extends Serializable> extends HibernateDaoSupport implements BaseDao<T> {

	@Autowired
	public void setSF(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}
	
	/**
	 * LOG4J日志
	 */
	protected final Logger logger = Logger.getLogger(this.getClass());
	
	/**
	 * 当前泛型实际实体类的CLASS对象
	 */
	private Class<T> persistentClass;
	
	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	public Class<T> getPersistentClass() {
		return persistentClass;
	}
	
	/**
	 * *******************************************************************************
	 * <big>以下是实体对象CURD操作</big>
	 * *******************************************************************************
	 */
	
	/**
	 * 根据ID获取对象. 实际调用Hibernate的session.load()方法返回实体或其proxy对象. 如果对象不存在，抛出异常.
	 */
	@SuppressWarnings("unchecked")
	private T get(Class<T> entityClass, Serializable id) {
		Assert.notNull(id);
//		return (T) getHibernateTemplate().load(entityClass, id);
		return (T) getHibernateTemplate().get(entityClass, id);                         //谢大云修改前期错误
	}
	
	/**
	 * 根据ID获取当前实体类
	 */
	@SuppressWarnings("unchecked")
	public T get(Serializable id) {
		Assert.notNull(id);
		return (T) get(getPersistentClass(), id);
	}
	
	/**
	 * 修改实体，更新数据值到数据库
	 * @param entity
	 * @return
	 */
	public T update(T entity) {
		Assert.notNull(entity);
		getHibernateTemplate().update(entity);
		return entity;
	}
	
	/**
	 * 修改或者保存当前对象
	 * 注意：修改后的对象是处于持久化状态
	 * 例如: User(name="u1"),修改后对象变成持久化状态即是加上userId,User(userId="001",name="u1")
	 * @param entity
	 * @return
	 */
	public T saveOrUpdate(T entity) {
		Assert.notNull(entity);
		getHibernateTemplate().saveOrUpdate(entity);
		return entity;
	}
	
	/**
	 * 批量修改或者保存脱管状态的实体
	 * @param entities
	 */
	public void saveOrUpdateAll(Collection entities){
		Assert.notEmpty(entities);
		this.getHibernateTemplate().saveOrUpdateAll(entities);
	}
	/**
	 * 保存对象.
	 */
	public T save(T entity) {
		getHibernateTemplate().save(entity);
		this.refresh(entity);
		return entity;
	}
	
	/**
	 * 修改或者保存当前对象
	 * 注意：修改后的对象是处于脱管状态，即不受SESSION管理
	 * 例如: User(name="u2"),修改后对象还是脱管状态即是User(name="u1")
	 * @param entity
	 * @return
	 */
	public T merge(T entity) {
		Assert.notNull(entity);
		return getHibernateTemplate().merge(entity);
	}
	
	/**
	 * 删除实体对象
	 * @param entity
	 */
	public void remove(T entity) {
		Assert.notNull(entity);
		getHibernateTemplate().delete(entity);
	}
	
	/**
	 * 根据实体主键，删除实体对象
	 * @param id
	 * @return
	 */
	public T removeById(Serializable id) {
		Assert.notNull(id);
		T entity=null;
		if(id instanceof Long){
			entity= load(id);			
		}else{
			entity=load(Integer.parseInt(id.toString()));
		}	
		getHibernateTemplate().delete(entity);
		return entity;
	}
	
	/**
	 * 批量删除实体集合
	 * @param entities
	 */
	public void removeAll(Collection entities){
		Assert.notEmpty(entities);
		this.getHibernateTemplate().deleteAll(entities);
	}
	
	/**
	 * 批量删除实体
	 * @param ids
	 * @return
	 */
	public List<T> removeAllByIds(List<Serializable> ids){
		Assert.notEmpty(ids);
		List<T> list = new ArrayList();
		for(Serializable id:ids){
			T entity=null;
			if(id instanceof Long){
				entity= load(id);			
			}else{
				entity=load(Integer.parseInt(id.toString()));
			}	
			list.add(entity);
			this.getHibernateTemplate().delete(entity);
		}
		return list;
	}
	
	/**
	 * 懒加载方式获取对象。主要作用在<one-to-many>的实体关系时候，将many的一端延迟加载，提高性能。
	 * 区别GET方式，一般采用GET()
	 * @param id
	 * @return
	 */
	public T load(Serializable id) {
		Assert.notNull(id);
		return load(id, false);
	}

	/**
	 * 懒加载方式获取对象。主要作用在<one-to-many>的实体关系时候，将many的一端延迟加载，提高性能。
	 * 区别GET方式，一般采用GET()
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T load(Serializable id, boolean lock) {
		Assert.notNull(id);
		T entity = null;
		if (lock) {
			entity = (T) getHibernateTemplate().load(getPersistentClass(), id,
					LockMode.UPGRADE);
		} else {
			entity = (T) getHibernateTemplate().load(getPersistentClass(), id);
		}
		return entity;
	}
	
	/**
	 * 获取全部对象.
	 */
	@SuppressWarnings("unchecked")
	private List<T> getAll(Class<T> entityClass) {
		return getHibernateTemplate().loadAll(entityClass);
	}
	
	/**
	 * 缺省值情况下获取全部对象
	 */
	public List<T> getAll() {
		return this.getAll(this.persistentClass);
	}
	
	

	/**
	 * 获取默认的全部实体
	 * @param <T>
	 * @param orderBy  排序的字段
	 * @param isAsc
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll(String orderBy, boolean isAsc) {
		Assert.hasText(orderBy);
		if (isAsc)
			return getHibernateTemplate().findByCriteria(
					DetachedCriteria.forClass(this.persistentClass).addOrder(Order.asc(orderBy)));
		else
			return getHibernateTemplate().findByCriteria(
					DetachedCriteria.forClass(this.persistentClass).addOrder(Order.desc(orderBy)));
	}
	
	
	/**
	 * 获取全部对象,带排序字段与升降序参数.
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll(Class<T> entityClass, String orderBy, boolean isAsc) {
		Assert.hasText(orderBy);
		if (isAsc)
			return getHibernateTemplate().findByCriteria(
					DetachedCriteria.forClass(entityClass).addOrder(Order.asc(orderBy)));
		else
			return getHibernateTemplate().findByCriteria(
					DetachedCriteria.forClass(entityClass).addOrder(Order.desc(orderBy)));
	}
	
	/**
	 * 输出SESSION所有对象，保存数据到数据库中
	 */
	public void flush() {
		getHibernateTemplate().flush();
	}
	
	/**
	 * 清空SESSION中的所有缓存
	 */
	public void clear() {
		getHibernateTemplate().clear();
	}
	
	/**
	 * 更新SESSION缓存对象
	 * @param entity
	 */
	public void refresh(T entity) {
		getSession().refresh(entity);
	}
	
	/**
	 * *******************************************************************************
	 * HQL组合复杂业务逻辑查询
	 * *******************************************************************************
	 */
	
	/**
	 * 创建Query对象. 对于需要first,max,fetchsize,cache,cacheRegion等诸多设置的函数,可以在返回Query后自行设置.
	 * 留意可以连续设置,如下：
	 * <pre>
	 * dao.getQuery(hql).setMaxResult(100).setCacheable(true).list();
	 * </pre>
	 * 调用方式如下：
	 * <pre>
	 *        dao.createQuery(hql)
	 *        dao.createQuery(hql,arg0);
	 *        dao.createQuery(hql,arg0,arg1);
	 *        dao.createQuery(hql,new Object[arg0,arg1,arg2])
	 * </pre>
	 * <strong>
	 * 	参数hql可以带上占位符：UPDATE User set sort=? where userId=?;
	 * </strong>
	 * @param values 可变参数.JDK1.5的新特性
	 */
	public Query createQuery(String hql, Object... values) {
		Assert.hasText(hql);
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query;
	}
	
	/**
	 * 创建Criteria对象.
	 *
	 * @param criterions 可变的Restrictions条件列表,见{@link #createQuery(String,Object...)}
	 */
	public  Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(this.persistentClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}
	
	/**
	 * 创建Criteria对象，带排序字段与升降序字段.
	 *
	 * @see #createCriteria(Class,Criterion[])
	 */
	public  Criteria createCriteria(String orderBy, boolean isAsc, Criterion... criterions) {
		Assert.hasText(orderBy);

		Criteria criteria = createCriteria(criterions);

		if (isAsc)
			criteria.addOrder(Order.asc(orderBy));
		else
			criteria.addOrder(Order.desc(orderBy));

		return criteria;
	}
	
	/**
	 * 根据hql查询,直接使用HibernateTemplate的find函数.
	 *
	 * @param values 可变参数,见{@link #createQuery(String,Object...)}
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(String hql, Object... values) {
		Assert.notNull(hql);
		return this.find(hql, SystemContext.getOffset(),SystemContext.getPagesize(),values);
	}
	
	
	
	@SuppressWarnings("unchecked")
	public List<T> find(final String hql,final int begin,final int maxSiz,Object... values){
		Assert.notNull(hql);
		String hqlStr = this.createQuery(hql, values).getQueryString();
		return (List<T>)this.getHibernateTemplate().execute(new HibernateCallback(){
			public Object doInHibernate(Session session)throws HibernateException {
					return session.createQuery(hql).setFirstResult(begin).setMaxResults(maxSiz).list();			
		    }
		});
	}
	
	/**
	 * 根据属性名和属性值查询对象.
	 *
	 * @return 符合条件的对象列表
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		return createCriteria(Restrictions.eq(propertyName, value)).list();
	}

	/**
	 * 根据属性名和属性值查询对象,带排序参数.
	 */
	@SuppressWarnings("unchecked")
	public  List<T> findByProperty(String propertyName, Object value, String orderBy, boolean isAsc) {
		Assert.hasText(propertyName);
		Assert.hasText(orderBy);
		return createCriteria(orderBy, isAsc, Restrictions.eq(propertyName, value)).list();
	}
	
	/**
	 * 根据属性名和属性值查询唯一对象.
	 *
	 * @return 符合条件的唯一对象 or null if not found.
	 */
	@SuppressWarnings("unchecked")
	public  T findUniqueByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		return (T) createCriteria(Restrictions.eq(propertyName, value)).uniqueResult();
	}
	
	/**
	 * 判断对象某些属性的值在数据库中是否唯一.
	 *
	 * @param uniquePropertyNames 在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 */
	public  boolean isUnique(Object entity, String uniquePropertyNames) {
		Assert.hasText(uniquePropertyNames);
		Criteria criteria = createCriteria().setProjection(Projections.rowCount());
		String[] nameList = uniquePropertyNames.split(",");
		try {
			// 循环加入唯一列
			for (String name : nameList) {
				criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
			}

			// 以下代码为了如果是update的情况,排除entity自身.

			String idName = getIdName();

			// 取得entity的主键值
			Serializable id = getId(entity);

			// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
			if (id != null)
				criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}
		return (Integer) criteria.uniqueResult() == 0;
	}
	
	/**
	 * 取得对象的主键值,辅助函数.
	 */
	public Serializable getId(Object entity){
		Assert.notNull(entity);
		try{
			return (Serializable) PropertyUtils.getProperty(entity, getIdName());
		}catch(Exception e){
			throw new PersistenceException(e);
		}
	}
	
	/**
	 * 取得对象的主键名,辅助函数.
	 */
	public String getIdName() {
		ClassMetadata meta = getSessionFactory().getClassMetadata(this.persistentClass);
		Assert.notNull(meta, "Class " + persistentClass + " not define in hibernate session factory.");
		String idName = meta.getIdentifierPropertyName();
		Assert.hasText(idName, persistentClass.getSimpleName() + " has no identifier property define.");
		return idName;
	}
	
	/**
	 * 根据属性查询条件获取记录行数
	 * @param property
	 * @param value
	 * @return
	 */
	public int countByProperty(String property, Object value) {
		Assert.hasText(property);
		Assert.notNull(value);
		return ((Number) (createCriteria(Restrictions.eq(property, value))
				.setProjection(Projections.rowCount()).uniqueResult()))
				.intValue();
	}
	
	/**
	 * 
	 * 获取数据行数的默认方法
	 * @return
	 */
	public long countAll(){
		return countAll(this.persistentClass);
	}
	
	/**
	 * 获取实体的数据行数
	 * @param entityClass
	 * @return
	 */
	public long countAll(Class<T> entityClass){
		Criteria criteria = getSession().createCriteria(entityClass);
		return (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
	}
	
	/**
	 * 创建新的实体对象
	 */
	public T createNewEntiey() {
		try {
			return getPersistentClass().newInstance();
		} catch (Exception e) {
			throw new RuntimeException("不能创建实体对象："
					+ getPersistentClass().getName());
		}
	}

	@SuppressWarnings("unchecked")
	private ClassMetadata getCmd(Class clazz) {
		return (ClassMetadata) this.getCmd(clazz);
	}
	
	/**
	 * *******************************************************************************
	 * 以下使用SQL作为查询，辅助HQL完成综合查询功能
	 * *******************************************************************************
	 */
	
	/**
	 * 使用SQL更新数据：insert or update op
	 */
	@SuppressWarnings("unchecked")
	public void updateBySql(String sql) {
		Assert.notNull(sql);
		Session session = null;
		try{
		session = this.getSession();
		SQLQuery query = session.createSQLQuery(sql);
		query.executeUpdate();
		}catch(Exception e){
			logger.error("通过SQL更新数据异常！出错位置updateBySql(String)中。");
			throw new PersistenceException(e.getMessage());
		}finally{
			this.releaseSession(session);
		}
	}
	
	/**
	 * 使用hql更新数据：insert or update op
	 */
	public void updateByHql(String hql){
		Assert.notNull(hql);
		Session session = null;
		try{
		session = this.getSession();
		Query query = session.createQuery(hql);
		query.executeUpdate();
		}catch(Exception e){
			logger.error("通过HQL更新数据异常！出错位置updateByHql(String)中。");
			throw new PersistenceException(e.getMessage());
		}finally{
			this.releaseSession(session);
		}
	}
	
	/**
	 * 使用SQL带上占位符的查询语句
	 * 如：UPDATE user_dept set sort=? where user_id=? and dept_id=?; 所以params数据的值就是对应着3个占位符的值
	 * @param sql
	 * @param params
	 * @throws 
	 */
	public void updateBySql(String sql,Object... params) {
		Assert.notNull(sql);
		Session session = null;
		try{
			session = this.getSession();
			logger.debug("sql = " + sql);
			SQLQuery query = session.createSQLQuery(sql);
			if (params != null) {
				int i = 0;
				for (Object obj : params) {
					query.setParameter(i, obj);
					i++;
				}
				i = 0;
			}
			query.executeUpdate();
		}catch(Exception e){
			logger.error("通过SQL更新数据异常！出错位置updateBySql(String,Object[])中。");
			throw new PersistenceException(e.getMessage());
		}finally{
			this.releaseSession(session);
		}
	}
	
	/**
	 * 使用SQL带上占位符的查询语句
	 * 如：UPDATE user_dept set sort=? where user_id=? and dept_id=?; 所以params数据的值就是对应着3个占位符的值
	 * @param sql
	 * @param params
	 * @throws 
	 */
	public void updateByHql(String hql,Object... params) {
		Assert.notNull(hql);
		Session session = null;
		try{
			session = this.getSession();
			logger.debug("sql = " + hql);
			Query query = session.createQuery(hql);
			if (params != null) {
				int i = 0;
				for (Object obj : params) {
					query.setParameter(i, obj);
					i++;
				}
				i = 0;
			}
			query.executeUpdate();
		}catch(Exception e){
			logger.error("通过Hql更新数据异常！出错位置updateByHql(String,Object[])中。");
			throw new PersistenceException(e.getMessage());
		}finally{
			this.releaseSession(session);
		}
	}
	
	/**
	 * 使用SQL查询结果，无分页信息
	 * 
	 * @param SQL
	 * @return
	 * @throws 
	 */
	@SuppressWarnings("unchecked")
	public List<Object[]> findBySQL(String sql) {
		Assert.notNull(sql);
		Session session = null;
		List<Object[]> result = null;
		try{
		session = super.getSession();
		logger.debug("sql = " + sql);
		result = session.createSQLQuery(sql).list();
		}catch(Exception e){
			logger.error("通过SQL查询数据异常！findBySQL(String)中。");
			throw new PersistenceException(e.getMessage());
		}finally{
			this.releaseSession(session);
		}
		return result;
	}
	
	/**
	 * 使用SQL查询结果，有分页信息
	 * 
	 * @param SQL
	 * @return
	 * @throws DBException
	 */
	@SuppressWarnings("unchecked")
	public List<Object[]> findBySQL(String sql, int beginIndex, int size){
		Assert.notNull(sql);
		Session session = null;
		List<Object[]> result = null;
		try{
			session = super.getSession();
			logger.debug("sql = " + sql);
			SQLQuery query = session.createSQLQuery(sql);
			query.setFirstResult(beginIndex);
			query.setMaxResults(size);
			result = query.list();
		}catch(Exception e){
			logger.error("通过SQL查询数据异常！findBySQL(String,int,int)中。");
			throw new PersistenceException(e.getMessage());
		}finally{
			this.releaseSession(session);
		}
		return result;
	}
	
	/**
	 * 通过HQL获取唯一的实体对象
	 * @param hql
	 * @return
	 * @throws DBException
	 */
	public Object uniqueResultByHQL(String hql) {
		Assert.notNull(hql);
		Session session = null;
		Object result = null;
		try {
			session = super.getSession();
			logger.debug("hql = " + hql);
			Query qry = session.createQuery(hql);
			result = qry.uniqueResult();
		} catch (Exception e) {
			logger.error("通过HQL查询数据异常！uniqueResultByHQL(String)中。");
			throw new PersistenceException(e.getMessage());
		} finally {
			super.releaseSession(session);
		}
		return result;
	}
	
	/**
	 * 通过SQL获取单一数据
	 * @param sql
	 * @return
	 * @throws DBException
	 */
	public Object uniqueResultBySQL(String sql) {
		Session session = null;
		Object result = null;
		try {
			session = super.getSession();
			logger.debug("sql = " + sql);
			Query qry = session.createSQLQuery(sql);
			result = qry.uniqueResult();
		} catch (Exception e) {
			throw new PersistenceException(e);
		} finally {
			super.releaseSession(session);
		}
		return result;
	}
	
	/**
	 * 解析查询总数sql语句
	 * @param sql
	 * @return
	 */
	protected String parseCountSql(String sql) {
		if(!sql.matches(".* (count|COUNT)\\(.+\\) .+"))//如果语句中已包含count函数，则不作任务解析(当一条统计语句比较复杂时，可自定义统计语句)，否则，按一般规则解析统计语句
		{
			sql = sql.replaceAll("(\\s|　)+", " ");
			int startIndex = sql.indexOf("from ");
			if(startIndex==-1)
				startIndex = sql.indexOf("FROM ");
			int endIndex = sql.lastIndexOf(" group by ");
			if(endIndex == -1)
			{
				endIndex = sql.lastIndexOf(" GROUP BY ");
			}
			if(endIndex == -1)
			{
				endIndex = sql.lastIndexOf(" order by ");
			}
			if(endIndex == -1)
			{
				endIndex = sql.lastIndexOf(" ORDER BY ");
			}
			if(endIndex==-1)
				sql = sql.substring(startIndex);
			else
				sql = sql.substring(startIndex, endIndex);
			
			sql = "select count(*) "+sql;
		}
		return sql;
	}
	
	/**
	 * 获取数据的总行数
	 * @param sql
	 * @return
	 * @throws DBException
	 */
	public int getRecordCountBySQL(String sql){
		Session session = null;
		sql = parseCountSql(sql);
		int result = 0;
		try {
			session = super.getSession();
			logger.debug("sql = " + sql);
			Query qry = session.createSQLQuery(sql);
			result = ((Number) qry.uniqueResult()).intValue();
		} catch (Exception e) {
			logger.error("获取SQL查询行数异常！getRecordCountBySQL(String)中。");
			throw new PersistenceException(e);
		} finally {
			super.releaseSession(session);
		}
		return result;
	}
}
